#include <iostream>
#include <string>
#include <functional>
#include <cstring> //strerror
#include <cerrno>  //errno
#include <cstdlib> //exit
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

namespace server
{
    typedef std::function<void(int, std::string, uint16_t, std::string)> func_t;
    enum
    {
        USAGE_ERROR = 1,
        SOCKET_ERROR,
        BIND_ERROR,
        OPEN_ERROE,
        CATLINE_ERROR

    };

    class udpServer
    {

        // const static std::string defaultIp ;
        static const std::string defaultIp;

    public:
        udpServer(func_t func, const uint16_t &port, const std::string ip = defaultIp)
            : _port(port), _ip(ip), _sockfd(-1), _func(func)
        {
        }

        ~udpServer()
        {
        }

        void initServer()
        {
            // 创建套接字。
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sockfd < 0)
            { // 创建失败
                std::cerr << "socket error!! " << errno << ": " << strerror(errno) << std::endl;
                exit(SOCKET_ERROR);
            }

            // 绑定ip+port
            struct sockaddr_in local; // sockaddr_in 使用的时候要包含头文件 <netinet/in.h> 或者 <arpa/inet.h>
            bzero(&local, sizeof(local));
            // 填入 协议家族，端口号，ip地址（uint32_t类型的)
            local.sin_family = AF_INET;    // 指定协议家族
            local.sin_port = htons(_port); // 指定端口号 //注意主机字节序转化为网络字节序
            // local.sin_addr.s_addr =inet_addr(_ip.c_str());
            local.sin_addr.s_addr = INADDR_ANY; // 任意地址绑定 服务器的真实写法

            // 指定ip（uint32_t) //注意1.点分十进制转化为uint32_t; 2.主机字节序转化为网络字节序。

            int ret = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
            // 注意强制类型转化，（struct sockaddr*)
            if (ret == -1)
            {
                std::cerr << "bind error!! " << errno << ": " << strerror(errno) << std::endl;
                exit(BIND_ERROR);
            }
            // 初始化完成
        }

        void startServer()
        {
            // 服务器的本质就是一个死循环。
            // 死循环的代码也叫常驻内存进程
            // 只有死循环的进程，不退出的进程，才会在乎内存泄漏。
            char buf[1024];

            for (;;)
            {
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer); // 这里不能省去。
                int s = recvfrom(_sockfd, buf, sizeof buf, 0, (struct sockaddr *)&peer, &len);
                if (s > 0)
                {
                    // 读取成功，
                    buf[s] = 0;
                    // 数据再buf中，客户端的信息在peer中
                    // 将客户端信息转化出来
                    std::string clinetip = inet_ntoa(peer.sin_addr);
                    uint16_t clientport = ntohs(peer.sin_port);
                    std::string message = buf;
                    _func(_sockfd, message, clientport, clinetip);
                    // 这里设置了一个对调函数，实现通讯和业务逻辑解耦的操作。
                }
            }
        }

    private:
        uint16_t _port;  // server——端口号
        std::string _ip; // server——ip
        int _sockfd;     // socket的返回值的文件描述符。
        func_t _func;    // 设置回调函数
    };
    const std::string udpServer::defaultIp = "0.0.0.0";
    // 静态成员一定要在类外面进行初始化。

} // server end

// #pragma once
// #include <iostream>
// #include <string>
// #include <cassert>
// #include <cstring>
// #include <stdlib.h>
// #include <cerrno>
// #include <strings.h>
// #include <sys/types.h>
// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <unistd.h>
// #include <arpa/inet.h>

// namespace server
// {
//     enum
//     {
//         USAGE_ERR = 1,
//         SOCKET_ERR = 2,
//         BIND_ERR = 3
//     };
//     static const std::string defaultIP = "0.0.0.0";
//     class UdpServer
//     {
//     public:
//         UdpServer(const uint16_t &port, const std::string ip = defaultIP) : _ip(ip), _port(port), _sockfd(-1)
//         {
//         }
//         void initserver()
//         {
//             int ret = socket(AF_INET, SOCK_DGRAM, 0); // 系统部分
//             // 参数1：选择域（本地还是，网络）； 参数2：选择流式服务还是，数据包服务，  参数3 ： 缺省为0即可。
//             // 成功返回一个文件描述符
//             // 失败返回-1，错误码被设置。
//             // 在我看来，这里和打开文件没有区别 ， Linux一切皆文件。
//             // 打开网卡和可开文件一样，
//             // 未来读写网络就和读写文件一样了
//             // assert(-1 != ret);
//             if (ret == -1)
//             {
//                 std::cout << "socket error: " << errno << ":" << strerror(errno) << std::endl;
//                 exit(SOCKET_ERR); // 退出码是自己设置的。
//             }
//             _sockfd = ret;
//             // 现在我们需要告诉对于上面打开的服务，的端口号是多少，ip是多少

//             struct sockaddr_in local;     // 在 用户栈 定义一个变量。
//             bzero(&local, sizeof(local)); // 按字节为单位向一个内存空间写 0
//             // 当然也可以memset，一样的。
//             local.sin_family = AF_INET; // 填写协议家族。

//             // local.sin_port = _port; // 如果给别人发消息，你的port和ip都要发给别人的。
//             // 这个_port 可能不是大端，需要转换。
//             local.sin_port = htons(_port);
//             // htons（主机字节序转换位网络字节序）。

//             // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 在网络中 IP 是uint32_t 表示的 整数。用户层面一般是点分十进制表示的字符串，。
//             local.sin_addr.s_addr = INADDR_ANY; // 绑定任意端口。//INADDR_ANY这个任意端口其实就是 0.0.0.0
//             // 需要转换 1. string -> uint32_t  2. htonl();
//             // inet_addr(_ip.c_str());  //1和2 都给完成了 //将字符串风格的点分十进制转换为：uint32_t的网络字节序的整数。
//             // C语言的结构体不能整体赋值。但是能整体被初始化。
//             // c++ 结构体（类）的可以整体赋值。

//             // 填写完成结构体之后，调用bind绑定
//             //  绑定port 和 ip
//             ret = bind(_sockfd, (struct sockaddr *)&local, sizeof(local)); // 网络部分
//             if (ret == -1)
//             {
//                 std::cout << "bind error: " << errno << ":" << strerror(errno) << std::endl;
//                 exit(BIND_ERR);
//             }
//         }

//         void startserver()
//         {
//             // 网络服务器的本质就是一个死循环。
//             // 一般死循环的进程（服务器），叫做常驻内存的进程，要避免内存泄漏的问题。这个很严重。
//             // 只有常驻内存的进程，才去考虑内存泄漏的问题。
//             // 操作系统（OS）本质就是一个死循环。常驻内存。
//             // 死循环对外提供服务。
//             for (;;)
//             {
//                 char buf[1024];
//                 sleep(1);
//                 struct sockaddr_in peer;
//                 socklen_t len = sizeof(peer);
//                 memset(&peer, 0, len);
//                 // 从网络sock读取数据。存入buf
//                 size_t s = recvfrom(_sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&(peer), &len);
//                 // 读取完成
//                 if (s > 0)
//                 {
//                     buf[s] = 0; // 当作字符串读取。
//                     std::string message = buf;
//                     std::string clientip = inet_ntoa(peer.sin_addr); // 1.网络字节序 -> 主机字节序 ；2. uint32_t -> string
//                     uint16_t clientport = ntohl(peer.sin_port);
//                     std::cout << clientip << "[" << clientport << "]#"
//                               << " message: " << message << std::endl;
//                 }
//             }
//         }

//         ~UdpServer()
//         {
//         }

//     private:
//         uint16_t _port;  // 保存当前服务器端口port号
//         std::string _ip; // 保存当前服务器ip
//         int _sockfd;     // 保存打开网卡对应的sockfd
//     };

// }

// // #pragma once

// // #include <iostream>
// // #include <string>
// // #include <strings.h>
// // #include <cerrno>
// // #include <cstring>
// // #include <cstdlib>
// // #include <functional>
// // #include <unistd.h>
// // #include <sys/types.h>
// // #include <sys/socket.h>
// // #include <arpa/inet.h>
// // #include <netinet/in.h>

// // namespace Server
// // {
// //     using namespace std;

// //     static const string defaultIp = "0.0.0.0"; //TODO
// //     static const int gnum = 1024;

// //     enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR};

// //     typedef function<void (string,uint16_t,string)> func_t;

// //     class udpServer
// //     {
// //     public:
// //         udpServer(const func_t &cb, const uint16_t &port, const string &ip = defaultIp)
// //         :_callback(cb), _port(port), _ip(ip), _sockfd(-1)
// //         {}
// //         void initServer()
// //         {
// //             // 1. 创建socket
// //             _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
// //             if(_sockfd == -1)
// //             {
// //                 cerr << "socket error: " << errno << " : " << strerror(errno) << endl;
// //                 exit(SOCKET_ERR);
// //             }
// //             cout << "socket success: " << " : " << _sockfd << endl;

// //             // 2. 绑定port，ip(TODO)
// //             // 未来服务器要明确的port，不能随意改变
// //             struct sockaddr_in local; // 定义了一个变量，栈，用户
// //             bzero(&local, sizeof(local));
// //             local.sin_family = AF_INET;
// //             local.sin_port = htons(_port); // 你如果要给别人发消息，你的port和ip要不要发送给对方
// //             local.sin_addr.s_addr = inet_addr(_ip.c_str());   // 1. string->uint32_t 2. htonl(); -> inet_addr
// //             //local.sin_addr.s_addr = htonl(INADDR_ANY); // 任意地址bind，服务器的真实写法
// //             int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
// //             if(n == -1)
// //             {
// //                 cerr << "bind error: " << errno << " : " << strerror(errno) << endl;
// //                 exit(BIND_ERR);
// //             }
// //             // UDP Server 的预备工作完成
// //         }
// //         void start()
// //         {
// //             // 服务器的本质其实就是一个死循环
// //             char buffer[gnum];
// //             for(;;)
// //             {
// //                 // 读取数据
// //                 struct sockaddr_in peer;
// //                 socklen_t len = sizeof(peer); //必填
// //                 ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
// //                 // 1. 数据是什么 2. 谁发的？
// //                 if(s > 0)
// //                 {
// //                     buffer[s] = 0;
// //                     string clientip = inet_ntoa(peer.sin_addr); //1. 网络序列 2. int->点分十进制IP
// //                     uint16_t clientport = ntohs(peer.sin_port);
// //                     string message = buffer;

// //                     cout << clientip <<"[" << clientport << "]# " << message << endl;
// //                     // 我们只把数据读上来就完了吗？对数据做处理
// //                     _callback(clientip, clientport, message);
// //                 }
// //             }
// //         }
// //         ~udpServer()
// //         {
// //         }
// //     private:
// //         uint16_t _port;
// //         string _ip; // 实际上，一款网络服务器，不建议指明一个IP
// //         int _sockfd;
// //         func_t _callback; //回调
// //     };
// // }